package com.acme.bankapp.domain.account;

import com.acme.bankapp.exceptions.OverDraftLimitExceededException;

/**
 * Class represents account that can grant a credit.
 *
 * @author S.Zhidkov
 */
public class CheckingAccount extends AbstractAccount{

    private static final String ERROR_OVERDRAFT = "Cannot set overdraft smaller than 0!";

    private final double overdraft;
    private double currentOverdraft;

    /**
     * Creates checking account
     *
     * @param id
     * @param amount
     * @param overdraft
     * @throws IllegalArgumentException if balance is negative
     */
    public CheckingAccount(final int id, final double amount, final double overdraft) {
        super(id, amount);
        if (overdraft < 0) {
            throw new IllegalArgumentException(ERROR_OVERDRAFT);
        }
        this.overdraft = overdraft;
        this.currentOverdraft = overdraft;
    }

    @Override
    public double maximumAmountToWithdraw() {
        return super.maximumAmountToWithdraw() + currentOverdraft;
    }

    @Override
    public void withdraw(double amount) throws OverDraftLimitExceededException {
        if (amount < 0) { throw new IllegalArgumentException(ERROR_AMOUNT_NEGATIVE); }

        if (amount <= balance) {
            balance -= amount;
            assert(balance >= 0);
            return;
        }

        if (amount <= (balance + currentOverdraft)) {
            amount -= balance;
            balance = 0;
            currentOverdraft -= amount;
            assert(balance >= 0);
            assert(currentOverdraft >= 0);
            return;
        }

        new OverDraftLimitExceededException(amount, this);
    }

    @Override
    public void deposit(double amount) throws IllegalArgumentException {
        if (amount < 0) { throw new IllegalArgumentException(ERROR_AMOUNT_NEGATIVE); }

        double delta = overdraft - currentOverdraft;
        if (amount <= delta) {
            currentOverdraft += amount;
        } else {
            amount -= delta;
            currentOverdraft = overdraft;
            balance += amount;
        }
    }

    @Override
    public double getTakedCredit() {
        return overdraft - currentOverdraft;
    }

    @Override
    public int hashCode() {
        long curOverdraftBits = Double.doubleToLongBits(currentOverdraft);
        long overdraftBits = Double.doubleToLongBits(overdraft);
        int result = 17;
        result = 37*result + (int) (curOverdraftBits ^ (curOverdraftBits >>> 32));
        result = 37*result + (int) (overdraftBits ^ (overdraftBits >>> 32));
        result = 37*result + super.hashCode();
        return result;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof CheckingAccount)) {
            return false;
        }

        final CheckingAccount ca = (CheckingAccount) o;
        if ((id != ca.id)) {
            return false;
        }
        if (Double.doubleToLongBits(balance) != Double.doubleToLongBits(ca.balance)) {
            return false;
        }
        if (Double.doubleToLongBits(overdraft) != Double.doubleToLongBits(ca.overdraft)) {
            return false;
        }
        if (Double.doubleToLongBits(currentOverdraft) != Double.doubleToLongBits(ca.currentOverdraft)) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return super.toString() + " [overdraft: " + currentOverdraft + "]";
    }
}
